package com.voltmao.webpro.utils;

import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.PropertyUtilsBean;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

public class PropertiesUtils {

    /**
     * 根据对象列表和对象的某个属性返回属性的List集合
     *
     * @param objList      对象列表
     * @param propertyName 要操作的属性名称
     * @return <pre>
     *  指定属性的List集合;
     *  如果objList为null或者size等于0抛出 IllegalArgumentException异常;
     *  如果propertyName为null抛出 IllegalArgumentException异常
     * </pre>
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    public static <T> List<Object> getPropertyList(List<T> objList, String propertyName) throws IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        if (objList == null || objList.size() == 0)
            throw new IllegalArgumentException("No objList specified");
        if (propertyName == null || "".equals(propertyName)) {
            throw new IllegalArgumentException("No propertyName specified for bean class '" + objList.get(0).getClass() + "'");
        }
        PropertyUtilsBean p = new PropertyUtilsBean();
        List<Object> propList = new LinkedList<Object>();
        for (int i = 0; i < objList.size(); i++) {
            T obj = objList.get(i);
            propList.add(p.getProperty(obj, propertyName));
        }
        return propList;
    }

    public static <T> List<String> getStringPropertyList(List<T> objList, String propertyName) {
        List<String> propList = new LinkedList<String>();

        if (objList == null || objList.size() == 0) {
            return propList;
        }

        if (propertyName == null || "".equals(propertyName)) {
            throw new IllegalArgumentException("No propertyName specified for bean class '" + objList.get(0).getClass() + "'");
        }
        PropertyUtilsBean p = new PropertyUtilsBean();

        for (int i = 0; i < objList.size(); i++) {
            T obj = objList.get(i);
            try {
                propList.add(String.valueOf(p.getProperty(obj, propertyName)));
            } catch (Exception e) {
                propList.add("null");
            }
        }
        return propList;
    }

    /**
     * 将List列表中的对象的某个属性封装成一个Map对象，key值是属性名，value值是对象列表中对象属性值的列表
     *
     * @param objList      对象列表
     * @param propertyName 属性名称,可以是一个或者多个
     * @return 返回封装了属性名称和属性值列表的Map对象，如果参数非法则抛出异常信息
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    public static <T> Map<String, List<Object>> getPropertiesMap(List<T> objList, String... propertyName)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        if (objList == null || objList.size() == 0)
            throw new IllegalArgumentException("No objList specified");
        if (propertyName == null || propertyName.length == 0) {
            throw new IllegalArgumentException("No propertyName specified for bean class '" + objList.get(0).getClass() + "'");
        }
        Map<String, List<Object>> maps = new HashMap<String, List<Object>>();
        for (int i = 0; i < propertyName.length; i++) {
            maps.put(propertyName[i], getPropertyList(objList, propertyName[i]));
        }
        return maps;
    }

    /**
     * Object2 List to Map<String, Object2> List
     *
     * @param <T>
     * @param objList
     * @param propertyNames
     * @return List<Map<String, Object2>>
     */
    public static <T> List<Map<String, Object>> objectListToListMap(List<T> objList, String... propertyNames) {
        if (objList == null || objList.size() == 0)
            throw new IllegalArgumentException("No objList specified");
        if (propertyNames == null || "".equals(propertyNames)) {
            throw new IllegalArgumentException("No propertyName specified for bean class '" + objList.get(0).getClass() + "'");
        }
        PropertyUtilsBean p = new PropertyUtilsBean();
        List<Map<String, Object>> rslist = new LinkedList<Map<String, Object>>();
        for (T obj : objList) {
            Map<String, Object> row = new HashMap<String, Object>();
            for (String name : propertyNames) {
                try {
                    row.put(name, p.getProperty(obj, name));
                } catch (Exception e) {
                    row.put(name, null);
                }
            }
            rslist.add(row);
        }

        return rslist;
    }


    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) {
        if (map == null)
            return null;
        Object obj = null;
        try {
            obj = beanClass.newInstance();
            ConvertUtils.register(new DateConverter(), Date.class);
            org.apache.commons.beanutils.BeanUtils.populate(obj, map);
        } catch (Exception e) {
            throw new RuntimeException("mapToObject ", e);
        }
        return obj;
    }

    /**
     * 对象转换为map
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, Object> objectToMap(Object obj) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        if (obj == null)
            return null;

        Map<String, Object> map = new HashMap<String, Object>();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(obj) : null;
            map.put(key, value);
        }

        return map;
    }
}

class DateConverter implements Converter {
    public Object convert(Class type, Object value) {
        if (value == null) {
            return null;
        } else if (type == Timestamp.class) {
            return convertToDate(type, value, "yyyy-MM-dd HH:mm:ss");
        } else if (type == Date.class) {
            return convertToDate(type, value, "yyyy-MM-dd");
        } else if (type == String.class) {
            return convertToString(type, value);
        }

        throw new ConversionException("不能转换 " + value.getClass().getName() + " 为 " + type.getName());
    }

    protected Object convertToDate(Class type, Object value, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        if (value instanceof String) {
            try {
                if (StringUtils.isEmpty(value.toString())) {
                    return null;
                }
                Date date = sdf.parse((String) value);
                if (type.equals(Timestamp.class)) {
                    return new Timestamp(date.getTime());
                }
                return date;
            } catch (Exception pe) {
                return null;
            }
        } else if (value instanceof Date) {
            return value;
        }

        throw new ConversionException("不能转换 " + value.getClass().getName() + " 为 " + type.getName());
    }

    protected Object convertToString(Class type, Object value) {
        if (value instanceof Date) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

            if (value instanceof Timestamp) {
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }

            try {
                return sdf.format(value);
            } catch (Exception e) {
                throw new ConversionException("日期转换为字符串时出错！");
            }
        } else {
            return value.toString();
        }
    }
}
